#pragma once
#include <iostream>
#include <memory>
#include <functional>
#include <etcd/Client.hpp>
#include <etcd/KeepAlive.hpp>
#include <etcd/Response.hpp>
#include <etcd/Watcher.hpp>
#include <etcd/Value.hpp>
#include "logger.h"

// 服务注册客户端类
class Register
{
public:
    using ptr = std::shared_ptr<Register>;
    
    Register(const std::string &host)
        : _cli(std::make_shared<etcd::Client>(host)), _keep_alive(_cli->leasekeepalive(5).get()), _lease_id(_keep_alive->Lease())
    {
    }

    // 注册函数
    bool registry(const std::string &key, const std::string &val)
    {
        // 向etcd新增数据
        auto resp = _cli->put(key, val, _lease_id).get();
        if (!resp.is_ok())
        {
            // std::cout << "新增数据失败: " << resp.error_message() << std::endl;
            LOG_ERROR("注册数据失败: {}", resp.error_message());
            return false;
        }
        return true;
    }

    ~Register()
    {
        _keep_alive->Cancel(); // 取消保活
    }

private:
    std::shared_ptr<etcd::Client> _cli;           // 客户端对象智能指针
    std::shared_ptr<etcd::KeepAlive> _keep_alive; // 租约保活对象智能指针
    uint64_t _lease_id;                           // 租约id
};

// 服务发现客户端类
class Discover
{
    using callback_t = std::function<void(std::string, std::string)>; // 回调函数类型
public:
    using ptr = std::shared_ptr<Discover>;

    Discover(const std::string host, const std::string basedir, const callback_t &put_cb, const callback_t &del_cb)
        : _cli(std::make_shared<etcd::Client>(host)), _put(put_cb), _del(del_cb)
    {
        // 先进行服务发现, 获取已有数据
        auto resp = _cli->ls(basedir).get();
        if (!resp.is_ok())
        {
            // std::cout << "获取数据失败: " << resp.error_message() << std::endl;
            LOG_ERROR("获取服务数据失败: {}", resp.error_message());
            exit(-1);
        }
        // 遍历
        int sz = resp.keys().size();
        for (int i = 0; i < sz; i++)
        {
            if (_put)
                _put(resp.key(i), resp.value(i).as_string());
        }
        // 再进行事件监控, 如果数据发生改变交给回调函数处理
        _watcher = std::make_shared<etcd::Watcher>(*_cli.get(), basedir, std::bind(&Discover::callback, this, std::placeholders::_1), true);
    }

    ~Discover()
    {
    }

private:
    void callback(const etcd::Response &resp)
    {
        if (!resp.is_ok())
        {
            // std::cout << "收到错误事件通知: " << resp.error_message() << std::endl;
            LOG_ERROR("收到错误事件通知: {}", resp.error_message());
            return;
        }
        for (auto const &ev : resp.events())
        {
            if (ev.event_type() == etcd::Event::EventType::PUT)
            {
                // 数据新增关心当前值
                if(_put)
                    _put(ev.kv().key(), ev.kv().as_string());
                LOG_DEBUG("新增服务: {}: {}", ev.kv().key(), ev.kv().as_string());
            }
            else if (ev.event_type() == etcd::Event::EventType::DELETE_)
            {
                // 数据删除关心上一次的值
                if(_del)
                    _del(ev.prev_kv().key(), ev.prev_kv().as_string());
                LOG_DEBUG("下线服务: {}: {}", ev.prev_kv().key(), ev.prev_kv().as_string());
            }
        }
    }

private:
    std::shared_ptr<etcd::Client> _cli;
    std::shared_ptr<etcd::Watcher> _watcher; // 键值对监控对象
    callback_t _put;                         // 回调函数
    callback_t _del;
};